package com.aizuda.easyManagerTool.service.monitor.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.aizuda.easy.security.domain.Rep;
import com.aizuda.easy.security.domain.Req;
import com.aizuda.easyManagerTool.config.CiphertextTypeHandler;
import com.aizuda.easyManagerTool.domain.dto.PageDTO;
import com.aizuda.easyManagerTool.domain.dto.monitor.MonitorEditDTO;
import com.aizuda.easyManagerTool.domain.entity.monitor.MonitorDataEntity;
import com.aizuda.easyManagerTool.domain.entity.monitor.MonitorDataIndexEntity;
import com.aizuda.easyManagerTool.domain.entity.monitor.MonitorDataIndexItemEntity;
import com.aizuda.easyManagerTool.domain.entity.monitor.MonitorUrlEntity;
import com.aizuda.easyManagerTool.domain.vo.PageVO;
import com.aizuda.easyManagerTool.domain.vo.setting.SettingUserVO;
import com.aizuda.easyManagerTool.domain.vo.socket.SocketMessageVO;
import com.aizuda.easyManagerTool.mapper.monitor.MonitorDataIndexItemMapper;
import com.aizuda.easyManagerTool.mapper.monitor.MonitorDataIndexMapper;
import com.aizuda.easyManagerTool.mapper.monitor.MonitorDataMapper;
import com.aizuda.easyManagerTool.mapper.monitor.MonitorUrlMapper;
import com.aizuda.easyManagerTool.service.monitor.MonitorAlarmService;
import com.aizuda.easyManagerTool.service.monitor.MonitorDataService;
import com.aizuda.easyManagerTool.service.socket.impl.SocketSessionManager;
import com.aizuda.easyManagerTool.util.AssertUtil;
import com.aizuda.easyManagerTool.util.ThreadPoolUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.nfunk.jep.JEP;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Slf4j
public class MonitorDataServiceImpl extends ServiceImpl<MonitorDataMapper, MonitorDataEntity> implements MonitorDataService {

    @Resource
    MonitorDataMapper monitorDataMapper;
    @Resource
    MonitorDataIndexMapper monitorDataIndexMapper;
    @Resource
    MonitorDataIndexItemMapper monitorDataIndexItemMapper;
    SocketMessageVO<String> socketMessageVO = new SocketMessageVO<String>();
    @Resource
    MonitorListManagerService monitorListManagerService;
    private ThreadPoolUtil executorService = new ThreadPoolUtil("监控拉取数据-");
    @Resource
    MonitorAlarmService monitorAlarmService;
    @Resource
    MonitorUrlMapper monitorUrlMapper;

    @org.springframework.beans.factory.annotation.Value("${config.data-pull-time}")
    Integer pullTime;

    @Override
    public Rep<PageVO<MonitorDataEntity>> find(Req<PageDTO<MonitorDataEntity>, SettingUserVO> req) {
        PageDTO<MonitorDataEntity> pageDTO = req.getData();
        Page<MonitorDataEntity> page = new Page<MonitorDataEntity>(pageDTO.getCurrent(), pageDTO.getSize());
        // 查询 唯一条件是要根据 权限查询
        MonitorDataEntity monitorDataEntity = pageDTO.getData() ;
        IPage<MonitorDataEntity> serverListEntrtyIPage = monitorDataMapper.find(page, ObjectUtil.isEmpty(monitorDataEntity)? new MonitorDataEntity():monitorDataEntity);
        PageVO<MonitorDataEntity> pageVO = new PageVO<MonitorDataEntity>(pageDTO)
                .setTotal(serverListEntrtyIPage.getTotal())
                .setRecords(serverListEntrtyIPage.getRecords());
        return Rep.ok(pageVO);
    }

    @Override
    public Rep<MonitorDataEntity> edit(Req<MonitorEditDTO, SettingUserVO> req) {
        editPrivate(req.getData());
        return Rep.ok();
    }

    @Override
    public void edit(MonitorEditDTO data){
        // 查询
        data.setMdStart(false);
        data.setMdSystem(true);
        data.setMdPullTime(5);
        editPrivate(data);
    }

    private void editPrivate(MonitorEditDTO data){
        AssertUtil.List.isEmpty(data.getUrls(), "请添加至少一个数据源地址");
        AssertUtil.objIsNull(data.getMdPullTime(),"拉取间隔不能为空");
        AssertUtil.objIsNull(data.getMdTitle(),"标题不能为空");
        saveOrUpdate(data);
        data.getUrls().stream().forEach(i ->{
            i.setMdId(data.getId());
            i.setUurl(CiphertextTypeHandler.encrypt(i.getUurl()));
        });
        monitorUrlMapper.deleteByMdId(data.getId());
        monitorUrlMapper.insertBatch(data.getUrls());
        // 判断是否启动，启动需要吧所有的index加入缓存
        if(data.getMdStart()){
            monitorListManagerService.updateMonitorData(data.getId());
        }else{
            MonitorListManager.remove(data.getId());
        }
    }

    @Override
    public Rep<MonitorDataEntity> del(Req<MonitorDataEntity, SettingUserVO> req) {
        MonitorDataEntity data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        MonitorDataEntity monitorDataEntity = monitorDataMapper.selectById(data.getId());
        delPrivate(monitorDataEntity);
        return Rep.ok();
    }

    private void delPrivate(MonitorDataEntity monitorDataEntity){
        MonitorListManager.remove(monitorDataEntity.getId());
        monitorUrlMapper.deleteByMdId(monitorDataEntity.getId());
        monitorDataMapper.deleteById(monitorDataEntity.getId());
        monitorDataIndexMapper.deleteByDataId(monitorDataEntity.getId());
        monitorDataIndexItemMapper.deleteByDataId(monitorDataEntity.getId());
    }

    @Override
    public Rep<Map<String,Object>> getUrl(Req<MonitorUrlEntity, SettingUserVO> req) {
        MonitorUrlEntity data = req.getData();
        AssertUtil.objIsNull(data.getUurl(),"url地址不能为空");
        AssertUtil.objIsNull(data.getUtype(),"请选择数据类型");
        Map<String, Object> map = new HashMap<>();
        try {
            map = urlToMap(data.getUurl(), data.getUtype());
        }catch(Exception e){
            return Rep.error(500,"无法连接，请查看服务是否启动或配置是否正确");
        }
        return Rep.ok(map);
    }


    @Override
    public void pull() {
        MonitorListManager.getValues().forEach(item -> {
            executorService.execute(() -> {
                // 数据转换
                MonitorListManager.MonitorData indexItem = MonitorListManager.getIndexItem(item.getId());
                if(CollUtil.isEmpty(indexItem.getUrls()) || CollUtil.isEmpty(item.getDataIndex())){
                    return;
                }
                Map<String, Object> map = new HashMap<>();
                indexItem.getUrls().parallelStream().forEach(k -> {
                    map.putAll(urlToMap(k.getUurl(), k.getUtype()));
                });
                indexItem.setJson(map);
            });
        });
    }

    @Override
    public void exec() {
        String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
        MonitorListManager.getValues().forEach(item -> {
            // 得到json
            executorService.execute(() -> {
                // 数据转换
                MonitorListManager.MonitorData indexItem = MonitorListManager.getIndexItem(item.getId());
                Map<String, Object> json = indexItem.getJson();
                if(ObjectUtil.isEmpty(json)){
                    return;
                }
                List<MonitorListManager.MonitorDataIndex> monitorDataIndices = dataConversion(json,indexItem,time);
                try {
                    // socket 推送
                    SocketMessageVO<Object> messageVO = BeanUtil.copyProperties(socketMessageVO, SocketMessageVO.class );
                    messageVO.setType("monitor:look");
                    messageVO.setObj(monitorDataIndices);
                    SocketSessionManager.sendToTenant(indexItem.getTenantId().toString()+":monitor", JSONUtil.toJsonStr(messageVO));
                }catch (Exception e){
                    e.printStackTrace();
                    log.error("5s 输出发送失败 {}",monitorDataIndices);
                    return;
                }
                // 告警保存
                monitorAlarmService.save(indexItem);
            });
        });
    }

    @Transactional
    @Override
    public Rep<MonitorDataEntity> clone(Req<MonitorEditDTO, SettingUserVO> req) {
        MonitorEditDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(), "数据错误");
        AssertUtil.objIsNull(data.getMdTitle(), "请填写数据源名称");
        AssertUtil.objIsNull(data.getId(), "请填写数据源地址");
        AssertUtil.List.isEmpty(data.getUrls(),"请添加至少一个数据源地址");
        MonitorDataEntity monitorDataEntity = monitorDataMapper.findById(data.getId());
        monitorDataEntity.setId(null);
        monitorDataEntity.setMdTitle(data.getMdTitle());
        monitorDataEntity.setMdStart(Boolean.FALSE);
        monitorDataEntity.setCreateTime(new Date());
        monitorDataEntity.setUpdateTime(new Date());
        monitorDataMapper.insert(monitorDataEntity);
        // 新增
        data.getUrls().forEach(i -> {
            i.setMdId(monitorDataEntity.getId());
            i.setUurl(CiphertextTypeHandler.encrypt(i.getUurl()));
        });
        monitorUrlMapper.insertBatch(data.getUrls());
        // 找出指标
        List<MonitorDataIndexEntity> monitorDataIndexEntities = monitorDataIndexMapper.findAll(data.getId());
        if(CollUtil.isNotEmpty(monitorDataIndexEntities)){
            monitorDataIndexEntities.forEach(i -> {
                // 找出指标项目
                List<MonitorDataIndexItemEntity> monitorDataIndexItemEntities = monitorDataIndexItemMapper.findByMdiId(i.getId());
                i.setId(null);
                i.setMdId(monitorDataEntity.getId());
                monitorDataIndexMapper.insert(i);
                if(CollUtil.isNotEmpty(monitorDataIndexItemEntities)){
                    List<MonitorDataIndexItemEntity> collect = monitorDataIndexItemEntities.stream().map(k -> {
                        k.setId(null);
                        k.setMdId(monitorDataEntity.getId());
                        k.setMdiId(i.getId());
                        return k;
                    }).collect(Collectors.toList());
                    monitorDataIndexItemMapper.insertBatch(collect);
                }
            });
        }
        return Rep.ok();
    }

    @Override
    public Rep<List<MonitorUrlEntity>> urls(Req<MonitorEditDTO, SettingUserVO> req) {
        MonitorEditDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        List<MonitorUrlEntity> monitorUrlEntities = monitorUrlMapper.selectByMdId(data.getId());
        return Rep.ok(monitorUrlEntities);
    }

    @Override
    public Rep<List<MonitorUrlEntity>> delUrls(Req<MonitorUrlEntity, SettingUserVO> req) {
        MonitorUrlEntity data = req.getData();
        AssertUtil.objIsNull(data.getId(), "数据错误");
        monitorUrlMapper.deleteId(data.getId());
        return Rep.ok();
    }

    @Override
    public Rep<List<MonitorUrlEntity>> changeSwitch(Req<MonitorEditDTO, SettingUserVO> req) {
        MonitorEditDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(), "数据错误");
        // 查找数据源地址
        List<MonitorUrlEntity> monitorUrlEntities = monitorUrlMapper.selectByMdId(data.getId());
        AssertUtil.List.isEmpty(monitorUrlEntities, "请添加至少一个数据源地址");
        updateById(data);
        if(data.getMdStart()){
            monitorListManagerService.updateMonitorData(data.getId());
        }else{
            MonitorListManager.remove(data.getId());
        }
        return Rep.ok();
    }

    @Override
    public Rep<Map<String, Object>> getAllUrl(Req<MonitorDataEntity, SettingUserVO> req) {
        MonitorDataEntity data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        List<MonitorUrlEntity> monitorUrlEntities = monitorUrlMapper.selectByMdId(data.getId());
        Map<String, Object> map = new HashMap<>();
        monitorUrlEntities.parallelStream().forEach(i -> map.putAll(urlToMap(i.getUurl(), i.getUtype())));
        return Rep.ok(map);
    }

    @Override
    public Rep<Map<String, Object>> getAllUrls(Req<MonitorEditDTO, SettingUserVO> req) {
        MonitorEditDTO data = req.getData();
        AssertUtil.List.isEmpty(data.getUrls(),"请至少添加一项数据源");
        Map<String, Object> map = new HashMap<>();
        data.getUrls().parallelStream().forEach(i -> map.putAll(urlToMap(i.getUurl(), i.getUtype())));
        return Rep.ok(map);
    }

    private List<MonitorListManager.MonitorDataIndex> dataConversion(Map<String, Object> map,MonitorListManager.MonitorData indexItem,String time){
        String jsonStr = JSONUtil.toJsonStr(map);
        // 数据转换
        return indexItem.getDataIndex().parallelStream().filter(i -> ObjectUtil.isNotEmpty(i.getId())).map(i -> {
            String value = "0.00";
            i.setTime(time);
            if(StrUtil.isNotEmpty(i.getExp()) && StrUtil.isNotEmpty(jsonStr) && !"{}".equals(jsonStr)){
                Context context = Context.create();
                Value result = context.eval("js", "let json = " + jsonStr + ";" + i.getExp());
                if (!result.isNull()) {
                    value = result.toString();
                }
            }
            i.setValue(value);
            if(ObjectUtil.isEmpty(i.getThreshold()) || !i.getMdiAlarm()){
                return i;
            }
            JEP jep = new JEP();
            //给变量赋值
            String exp = i.getValue()+" "+i.getThreshold();
            exp = exp.replaceAll("([a-zA-Z_]+\\d*)", "\"$1\"");
            jep.parseExpression(exp);
            if(jep.getValue() != 0.0){
                i.setCurrentFrequency(i.getCurrentFrequency()+1);
            }else{
                i.setCurrentFrequency(0);
            }
            return i;
        }).collect(Collectors.toList());
    }

    private Map<String,Object> urlToMap(String url,String type){
        Map<String,Object> map = new ConcurrentHashMap<>();
        String s = HttpUtil.get(url, pullTime);
        switch (type){
            case "metrics": map = convertToJSON(s); break;
            case "json": map = JSONUtil.toBean(s,Map.class);
            default: break;
        }
        return map;
    }

    public static Map<String,Object> convertToJSON(String prometheusMetrics) {
        String[] lines = prometheusMetrics.trim().split("\n");
        Map<String, Object> metric = new HashMap<>();
        Arrays.stream(lines).filter(item -> !item.contains("#"))
                .forEach(item -> {
                    String[] s = item.split(" ");
                    for(int i=1;i< s.length - 1;i++){
                        s[0] = s[0]+ s[i];
                    }
                    String key = s[0].substring(0, s[0].indexOf("{") != -1? s[0].indexOf("{") : s[0].length()).trim();
                    String value = s[s.length - 1].trim();
                    Map<String, Object> kv = new HashMap<>();
                    kv.put("value",Double.parseDouble(value));
                    if(s[0].indexOf("{") != -1) {
                        String[] split = s[0].replace(key, "")
                                .replace("{", "")
                                .replace("}", "").split(",");
                        Arrays.stream(split).forEach(ik -> {
                            try {
                                String[] ikSplit = ik.split("=");
                                kv.put(ikSplit[0], ikSplit[1].replace("\"",""));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        });
                    }
                    Object keyObj = metric.get(key);
                    if(ObjectUtil.isEmpty(keyObj)){
                        metric.put(key,kv);
                    }else{
                        Object o = metric.get(key);
                        if(o instanceof List){
                            ((List) o).add(kv);
                        }else{
                            ArrayList<Object> objects = new ArrayList<>();
                            objects.add(kv);
                            objects.add(o);
                            metric.put(key,objects);
                        }
                    }
                });
        return metric;
    }

}
